#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
这个脚本用于将concepts.json文件转换为Markdown格式。
它支持处理多种内容类型，包括段落文本、列表、图片、代码示例等。
使用面向对象的方式实现，提高代码的可维护性和可扩展性。
"""

import json
import os
import sys
from typing import Dict, List, Any, Optional


class ContentProcessor:
    """内容处理器基类，定义内容处理的基本接口"""
    
    def process(self, content: Any) -> str:
        """处理内容的抽象方法
        
        Args:
            content: 要处理的内容
            
        Returns:
            str: 处理后的markdown文本
        """
        raise NotImplementedError("子类必须实现process方法")


class ParagraphProcessor(ContentProcessor):
    """段落处理器"""
    
    def __init__(self, formats: Optional[Dict] = None):
        """初始化段落处理器
        
        Args:
            formats: 包含链接和代码块信息的字典
        """
        self.formats = formats or {}
    
    def process(self, content: Dict) -> str:
        """处理段落内容，包括文本、代码块和链接
        
        Args:
            content: 段落内容字典，包含value、code_blocks和links属性
            
        Returns:
            str: 处理后的markdown文本，始终以两个换行结束
        """
        try:
            # 获取段落文本内容
            text = content.get('value', '')
            if not text:
                return ""
            
            # 处理代码块
            code_blocks = content.get('code_blocks', [])
            if code_blocks and isinstance(code_blocks, list):
                for code_block_id in code_blocks:
                    if not isinstance(code_block_id, str):
                        continue
                    # 从formats中获取代码块信息
                    block_info = self.formats.get('code_blocks', {}).get(code_block_id, {})
                    if block_info:
                        # 获取代码块中的文本
                        code_text = block_info.get('content', '').strip()
                        # 在原始文本中查找并替换为代码格式
                        if code_text in text:
                            text = text.replace(code_text, f"`{code_text}`")
            
            # 处理链接
            links = content.get('links', [])
            if links and isinstance(links, list):
                for link_id in links:
                    if not isinstance(link_id, str):
                        continue
                    # 从formats中获取链接信息
                    link_info = self.formats.get('links', {}).get(link_id, {})
                    if link_info:
                        link_name = link_info.get('name', '')
                        link_href = link_info.get('href', '')
                        # 在原始文本中查找并替换为markdown链接格式
                        if link_name and link_name in text:
                            text = text.replace(link_name, f"[{link_name}]({link_href})")
                        else:
                            # 如果找不到name, 仍然尝试替换占位符
                            placeholder = f"<link>{link_id}</link>"
                            replacement = f"[{link_name}]({link_href})"
                            text = text.replace(placeholder, replacement)
            
            # 确保段落后有两个换行符，以便在Markdown中形成段落间距
            return f"{text}\n\n"
        except Exception as e:
            print(f"Error processing paragraph: {str(e)}")
            return ""


class ListProcessor(ContentProcessor):
    """列表处理器"""
    
    def process(self, content: Dict, indent_level: int = 0) -> str:
        """处理列表内容，支持嵌套列表
        
        Args:
            content: 列表内容字典，包含items属性
            indent_level: 缩进级别，用于处理嵌套列表
            
        Returns:
            str: 处理后的markdown文本，始终以一个额外的换行结束
        """
        try:
            result = []
            items = content.get('items', [])
            
            if not isinstance(items, list):
                return ""
            
            indent = "  " * indent_level
            
            for item in items:
                if isinstance(item, dict):
                    # 处理列表项文本
                    text = item.get('text', '')
                    if text:
                        result.append(f"{indent}- {text}\n")
                    
                    # 处理嵌套列表
                    if 'items' in item or item.get('type') == 'ulist':
                        # 如果存在子项或是嵌套的ulist
                        nested_items = item.get('items', [])
                        if nested_items and isinstance(nested_items, list):
                            # 创建一个新的嵌套内容
                            nested_content = {'items': nested_items}
                            # 递归处理嵌套列表，增加缩进级别
                            result.append(self.process(nested_content, indent_level + 1))
            
            # 如果是顶层列表（非嵌套），添加额外的换行
            if indent_level == 0:
                return ''.join(result) + "\n"
            else:
                return ''.join(result)
        except Exception as e:
            print(f"Error processing list: {str(e)}")
            return ""


class ImageProcessor(ContentProcessor):
    """图片处理器"""
    
    def __init__(self, formats: Dict):
        self.formats = formats
    
    def process(self, content: Dict) -> str:
        """处理图片内容
        
        Args:
            content: 图片内容字典，包含value属性
            
        Returns:
            str: 处理后的markdown图片标签，以两个换行结束
        """
        try:
            image_value = content.get('value')
            if not image_value or not isinstance(image_value, str):
                return ""
                
            image_info = self.formats.get('imageblock', {}).get(image_value, {})
            if image_info:
                return f"![{image_info.get('alt', '')}]({image_info.get('url', '')})\n\n"
            return ""
        except Exception as e:
            print(f"Error processing image: {str(e)}")
            return ""


class CodeExampleProcessor(ContentProcessor):
    """代码示例处理器"""
    
    def __init__(self, formats: Dict):
        self.formats = formats
    
    def process(self, content: Dict) -> str:
        """处理代码示例内容
        
        Args:
            content: 代码示例内容字典，包含value属性
            
        Returns:
            str: 处理后的markdown代码块，以两个换行结束
        """
        try:
            code_value = content.get('value')
            if not code_value or not isinstance(code_value, str):
                return ""
                
            code_info = self.formats.get('code_examples', {}).get(code_value, {})
            if code_info:
                return f"```{code_info.get('language', '')}\n{code_info.get('content', '')}\n```\n\n"
            return ""
        except Exception as e:
            print(f"Error processing code example: {str(e)}")
            return ""


class CodeBlockProcessor(ContentProcessor):
    """代码块处理器"""
    
    def __init__(self, formats: Dict):
        self.formats = formats
    
    def process(self, content: Dict) -> str:
        """处理代码块内容
        
        Args:
            content: 代码块内容字典，包含code_blocks属性
            
        Returns:
            str: 处理后的markdown代码块集合，以两个换行结束
        """
        try:
            result = []
            code_blocks = content.get('code_blocks', [])
            if not isinstance(code_blocks, list):
                return ""
                
            for code_block in code_blocks:
                if isinstance(code_block, str):
                    block_info = self.formats.get('code_blocks', {}).get(code_block, {})
                    if block_info:
                        result.append(f"```{block_info.get('language', '')}\n{block_info.get('content', '')}\n```\n")
                elif isinstance(code_block, dict):
                    result.append(f"```{code_block.get('language', '')}\n{code_block.get('content', '')}\n```\n")
            
            # 在所有代码块后添加额外的换行
            if result:
                return ''.join(result) + "\n"
            return ""
        except Exception as e:
            print(f"Error processing code block: {str(e)}")
            return ""


class MarkdownGenerator:
    """Markdown生成器，负责整体转换流程"""
    
    def __init__(self):
        self.processors = {
            'paragraph': None,  # 将在set_formats中设置
            'ulist': ListProcessor(),
            'imageblock': None,  # 将在set_formats中设置
            'code_examples': None,  # 将在set_formats中设置
            'code_blocks': None,  # 将在set_formats中设置
        }
    
    def set_formats(self, formats: Dict):
        """设置formats数据，初始化需要formats的处理器
        
        Args:
            formats: 包含各种格式信息的字典
        """
        self.processors['paragraph'] = ParagraphProcessor(formats)
        self.processors['imageblock'] = ImageProcessor(formats)
        self.processors['code_examples'] = CodeExampleProcessor(formats)
        self.processors['code_blocks'] = CodeBlockProcessor(formats)
    
    def process_section(self, section: Dict) -> List[str]:
        """处理单个章节
        
        Args:
            section: 章节内容字典
            
        Returns:
            List[str]: 处理后的markdown文本列表
        """
        md_content = []
        
        # 添加章节标题
        if section.get('title'):
            md_content.append(f"## {section['title']}\n")
        
        # 处理正文内容
        if 'body' in section:
            for item in section['body']:
                item_type = item.get('type')
                processor = self.processors.get(item_type)
                if processor:
                    # 对于ulist类型的项，确保正确处理
                    if item_type == 'ulist':
                        md_content.append(processor.process(item, indent_level=0))
                    else:
                        md_content.append(processor.process(item))
                else:
                    print(f"Warning: No processor found for item type: {item_type}")
        
        # 递归处理子章节
        if 'sections' in section:
            for subsection in section['sections']:
                md_content.extend(self.process_section(subsection))
        
        return md_content
    
    def generate(self, data: Dict, weight: int = 1, is_index: bool = False, nav_title: str = None) -> str:
        """生成完整的markdown内容
        
        Args:
            data: 输入的JSON数据
            weight: 权重值
            is_index: 是否为_index.md文件
            nav_title: 导航菜单中的标题，如果提供则优先使用
            
        Returns:
            str: 生成的markdown文本
        """
        # 设置formats数据
        self.set_formats(data['formats'])
        
        # 使用导航菜单标题或JSON数据中的标题
        title = nav_title if (nav_title and (data.get('title') is None or data.get('title') == '')) else data.get('title', '')
        
        # 添加YAML front matter
        md_content = [
            "---\n",
            f"title: \"{title}\"\n",
            f"weight: {weight}\n"
        ]
        
        # 如果是_index.md文件，添加bookCollapseSection属性
        if is_index:
            md_content.append("bookCollapseSection: true\n")
        
        md_content.append("---\n\n")
        
        # 从主标题开始
        md_content.append(f"# {title}\n")
        
        # 处理简介部分
        if 'introduction' in data['content']:
            intro = data['content']['introduction']
            # 处理introduction为字典的情况
            if isinstance(intro, dict):
                if intro.get('title'):
                    md_content.append(f"## {intro['title']}\n")
                if 'body' in intro:
                    for item in intro['body']:
                        item_type = item.get('type')
                        processor = self.processors.get(item_type)
                        if processor:
                            # 对于ulist类型的项，确保正确处理
                            if item_type == 'ulist':
                                md_content.append(processor.process(item, indent_level=0))
                            else:
                                md_content.append(processor.process(item))
            # 处理introduction为列表的情况
            elif isinstance(intro, list):
                for item in intro:
                    if isinstance(item, dict):
                        item_type = item.get('type')
                        processor = self.processors.get(item_type)
                        if processor:
                            # 对于ulist类型的项，确保正确处理
                            if item_type == 'ulist':
                                md_content.append(processor.process(item, indent_level=0))
                            else:
                                md_content.append(processor.process(item))
        
        # 处理主要章节
        if 'sections' in data['content']:
            for section in data['content']['sections']:
                md_content.extend(self.process_section(section))
        
        return ''.join(md_content)


class JsonToMarkdownConverter:
    """JSON到Markdown的转换器，处理文件IO和转换流程"""
    
    def __init__(self, input_file: str, output_file: str, weight: int = 1, is_index: bool = False, nav_title: str = None):
        """初始化转换器
        
        Args:
            input_file: 输入JSON文件路径
            output_file: 输出Markdown文件路径
            weight: 权重值
            is_index: 是否为_index.md文件
            nav_title: 导航菜单中的标题，优先于JSON文件中的标题
        """
        self.input_file = input_file
        self.output_file = output_file
        self.weight = weight
        self.is_index = is_index
        self.nav_title = nav_title
        self.generator = MarkdownGenerator()
    
    def convert(self) -> None:
        """执行转换过程"""
        try:
            # 读取JSON文件
            print(f"Reading file: {self.input_file}")
            with open(self.input_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
            
            # 生成markdown内容，传入导航标题
            print("Generating markdown content...")
            markdown_content = self.generator.generate(data, self.weight, self.is_index, self.nav_title)
            
            # 确保输出目录存在
            os.makedirs(os.path.dirname(self.output_file), exist_ok=True)
            
            # 写入输出文件
            with open(self.output_file, 'w', encoding='utf-8') as f:
                f.write(markdown_content)
            print(f"Success: Markdown content has been generated and saved to {self.output_file}")
            
        except FileNotFoundError:
            print(f"Error: File not found: {self.input_file}")
            sys.exit(1)
        except json.JSONDecodeError as e:
            print(f"Error: Invalid JSON format in {self.input_file}: {str(e)}")
            sys.exit(1)
        except Exception as e:
            print(f"Error: An unexpected error occurred: {str(e)}")
            import traceback
            traceback.print_exc()
            sys.exit(1)


class NavigationProcessor:
    """导航处理器，负责按照导航菜单结构处理文件"""
    
    def __init__(self, source_dir: str, output_dir: str):
        """初始化导航处理器
        
        Args:
            source_dir: 源目录路径
            output_dir: 输出目录路径
        """
        self.source_dir = source_dir
        self.output_dir = output_dir
        self.generator = MarkdownGenerator()
    
    def process_nav_item(self, item: Dict, parent_path: str = "") -> None:
        """处理导航菜单项
        
        Args:
            item: 导航菜单项
            parent_path: 父级路径
        """
        # 获取当前项的路径和标题
        path = item.get('path', '')
        title = item.get('title', '')
        weight = item.get('weight', 1)
        
        if not title:
            return
        
        # 检查是否是特殊路径格式 (#XXX)
        is_hash_path = path.startswith('#')
        
        # 非特殊路径则正常处理
        if not is_hash_path:
            path = path.replace('.html', '.json')
            
        # 如果路径不存在，但是有效的标题，仍然继续处理子项
        if not path and title:
            path = "#"
            is_hash_path = True
        
        # 构建完整的输入和输出路径
        json_file = os.path.join(self.source_dir, path) if not is_hash_path else None
        md_file = os.path.join(self.output_dir, parent_path, f"{title}.md")
        
        # 确保输出目录存在
        os.makedirs(os.path.dirname(md_file), exist_ok=True)
        
        # 如果有子项，创建目录并处理_index.md
        if 'children' in item:
            # 创建目录
            dir_path = os.path.join(self.output_dir, parent_path, title)
            os.makedirs(dir_path, exist_ok=True)
            
            # 处理_index.md
            index_file = os.path.join(dir_path, '_index.md')
            
            if is_hash_path:
                # 对于 #XXX 格式的路径，创建空的 _index.md 文件，只包含 front matter
                print(f"Creating empty index file for path {path}: {index_file}")
                with open(index_file, 'w', encoding='utf-8') as f:
                    f.write("---\n")
                    f.write(f"title: \"{title}\"\n")
                    f.write(f"weight: {weight}\n")
                    f.write("bookCollapseSection: true\n")
                    f.write("---\n\n")
                    f.write(f"# {title}\n")
            elif json_file and os.path.exists(json_file):
                # 处理常规的索引文件
                print(f"Processing index file: {json_file}")
                converter = JsonToMarkdownConverter(json_file, index_file, weight, is_index=True, nav_title=title)
                converter.convert()
            
            # 递归处理子项
            for child in item['children']:
                self.process_nav_item(child, os.path.join(parent_path, title))
        else:
            # 处理普通文件
            if is_hash_path:
                # 对于 #XXX 格式的路径，创建空的 md 文件
                print(f"Creating empty markdown file for path {path}: {md_file}")
                with open(md_file, 'w', encoding='utf-8') as f:
                    f.write("---\n")
                    f.write(f"title: \"{title}\"\n")
                    f.write(f"weight: {weight}\n")
                    f.write("---\n\n")
                    f.write(f"# {title}\n")
            elif json_file and os.path.exists(json_file):
                print(f"Processing file: {json_file}")
                converter = JsonToMarkdownConverter(json_file, md_file, weight, nav_title=title)
                converter.convert()
    
    def process(self) -> None:
        """处理导航菜单"""
        # 读取导航菜单文件
        nav_file = os.path.join(self.source_dir, 'nav_menu.json')
        if not os.path.exists(nav_file):
            print(f"Error: Navigation menu file not found: {nav_file}")
            return
        
        try:
            with open(nav_file, 'r', encoding='utf-8') as f:
                nav_data = json.load(f)
            
            # 按weight排序
            nav_items = sorted(nav_data, key=lambda x: x.get('weight', 0))
            
            # 处理每个导航项
            for item in nav_items:
                self.process_nav_item(item)
                
        except Exception as e:
            print(f"Error processing navigation menu: {str(e)}")
            import traceback
            traceback.print_exc()


def main():
    """主函数：处理命令行参数并执行转换过程"""
    # 源目录和输出目录
    source_dir = 'source/spring-ai/1.0'
    output_dir = 'output/spring-ai/1.0/content'
    
    # 确保输出目录存在
    os.makedirs(output_dir, exist_ok=True)
    
    # 使用导航处理器处理文件
    processor = NavigationProcessor(source_dir, output_dir)
    processor.process()


if __name__ == "__main__":
    main()
